package de.mmis.core.tuplespace;

import java.util.Map;

import de.mmis.core.base.DeserializableAnnotations;
import de.mmis.core.base.abstracttree.InnerNode;
import de.mmis.core.base.abstracttree.LeafNode;
import de.mmis.core.base.abstracttree.Tree;
import de.mmis.core.base.abstracttree.serializer.DeserializableException;
import de.mmis.core.base.abstracttree.serializer.Serializable;

/**
 * Instances of this class are used to subscribe to changes of Tuples in a
 * {@link Tuplespace}. Notifications consist of a template (only Tuples matching
 * it generate an event) and a command, saying on which type of change (write,
 * take or both) an event should be generated.
 * 
 * @author Martin Nyolt
 * 
 */
public class Notification implements Serializable, DeserializableAnnotations {

	/**
	 * Used to define if an event should only be generated when a tuple has been
	 * written or taken from the space.
	 * 
	 * @author Martin Nyolt
	 * 
	 */
	public enum Command {
		WRITE, TAKE, BOTH
	}

	/** A wild card to be used for Notifications. */
	public static final LeafNode WILDCARD = new LeafNode("?");

	private final Command command;
	private final Tree template;

	/**
	 * convenience constructor for write events. It's the same as <br/>
	 * <code>new Notification(Command.WRITE, template)</code>
	 * 
	 * @param template
	 */
	public Notification(Tree template) {
		this(Command.WRITE, template);
	}

	/**
	 * create a new Notification instance
	 * 
	 * @param command
	 * @param template
	 */
	public Notification(Command command, Tree template) {
		this.command = command;
		this.template = template;
	}

	/**
	 * @return the command
	 */
	public Command getCommand() {
		return command;
	}

	/**
	 * @return the template
	 */
	public Tree getTemplate() {
		return template;
	}

	@Override
	public boolean equals(Object obj) {
		if (!(obj instanceof Notification))
			return false;

		Notification other = (Notification) obj;
		return command.equals(other.command) && template.equals(other.template);
	}

	@Override
	public int hashCode() {
		return template.hashCode() + command.ordinal();
	}

	@Override
	public Tree[] serialize(boolean explicit, Map<Object, String> refmap,
			ClassLoader classLoader) {
		return new Tree[] {
				Tree.fromObject(command, false, refmap, classLoader), template };
	}

	@DeserializationMethod
	public static Object deserialize(Tree[] e, ClassLoader classLoader,
			String ownID, Map<String, Object> idmap)
			throws DeserializableException {
		if (e.length != 2)
			throw new DeserializableException(new InnerNode(e),
					"Notifications must have 2 parameters");

		Tree sub0 = e[0];
		Command command = sub0.getAs(Command.class);

		return new Notification(command, e[1]);
	}

	@Override
	public String getTypeIdentifier() {
		return "tuplespace-notification";
	}
}
